/*
 * ecmconf.c - ECM configuration setup tool
 * cezanne@clunix.com, 2006.6
 *
 * Copyright (c) 2001-2011 by Clunix Inc. All Rights Reserved.
 */

#include "config.h"

#include "sr.h"
#include "eds.h"

#include "ecmconf.h"

static char	*clname;
static char	*ndname;
static ecm_uint16	ebc_port;
static ecm_int32	ebcd_detection_interval;
static ecm_uint16	egid_port;
static ecm_int32	egid_refresh_rate;
static ecm_int32	egid_session_timeout;
static char	*egid_login_page;
static char	*egid_top_page;
static char	*bdcast_addr;
static ECMBOOL	master_enabled;
static ECMBOOL	is_update_master_enabled;

static ECMBOOL		remove_all_static_routes;
static sr_t		*sr_static_routes;

static ECMBOOL	verbose = ECM_TRUE;
ECMBOOL	force;

char	*econf_path;
sr_t	*sr_conf;

static sr_t	*sr_daemon_infos;

static char	*command;

static void
usage(void)
{
	ecm_fprintf(ecm_stderr,
"Usage: ecmconf <options> [<command>]\n"
" Command: show | check | update | install | remove | status\n"
"   show: show econf (default command)\n"
"   check: check econf\n"
"   update: update econf\n"
"   install: ECM daemon install and start\n"
"   remove: remove ECM daemon\n"
"   status: report ECM daemon status\n"
"\n"
" Options:\n"
"  -n <nd name>:    node name\n"
"  -c <cluster name>\n"
"  -p <ebc port>:   ebc port number\n"
"  -e <egid port>:  egid port number\n"
"  -b <broadcast addr>\n"
"  -m <1 or 0>:     set this node as master-enabled(1) or slave-only(0)\n" 
"  -R <egid refresh rate in sec>\n"
"  -t <timeout>:    egid_session timeout\n"
"  -i <detection interval>: ebcd detection interval\n"
"  -L <egid_login_page>\n"
"  -T <egid_top_page>\n"
"  -S <svcname>: service name for ECM daemon\n"
"     svcname: eg) edbd=edbdX\n"
"     This option can be specified multiple times.\n"
"  -A <arg>: service argument\n"
"     arg: eg) edbd=-l -p 10001\n"
"     This option can be specified multiple times.\n"
"  -r <ebcd static route spec>\n"
"     Static route spec eg) ndname,sock/tcp/127.0.0.1:908\n"
"     This option can be specified multiple times.\n"
"     Empty argument will remove all existing static routes.\n"
"  -q: quiet mode\n"
"  -f: force mode\n"
"  -h: display this messages\n"
	);
}

static void
print_err(const char *fmt, ...)
{
	va_list	ap;
	char	*buf;

	va_start(ap, fmt);
	ecm_vasprintf(&buf, fmt, ap);
	va_end(ap);
	ecm_fprintf(ecm_stderr, "%s\n", buf);
	ecm_free(buf);
}

void
print_verbose(const char *fmt, ...)
{
	va_list	ap;
	char	*buf;

	if (!verbose)
		return;

	va_start(ap, fmt);
	ecm_vasprintf(&buf, fmt, ap);
	va_end(ap);
	ecm_fprintf(ecm_stdout, "%s\n", buf);
	ecm_free(buf);
}

static void
setup_service_name_arg(char *optarg, const char *child_name)
{
	sr_t	*sr_info;
	char	*arg;
	char	*p;

	if ((p = ecm_strchr(optarg, '=')) == NULL) {
		print_err("%s: invalid argument. name=value required", optarg);
		ecm_exit(1);
	}
	*p = '\0';
	sr_info = sr_getc(sr_daemon_infos, optarg);
	if (sr_info == NULL) {
		print_err("%s: invalid daemon name", optarg);
		ecm_exit(1);
	}
	arg = p + 1;

	if (ecm_empty_str(arg))
		return;
	sr_setc(sr_info, child_name, sr_create_str(ST_STR, arg));
}

static void
apply_service_name_arg(void)
{
	sr_t	*sr_info, *sr_info_conf;
	const char	*daemon_name;
	const char	*svcname;
	const char	*arg;

	SR_FOREACH_CHILD (sr_daemon_infos, NULL, sr_info) {
		daemon_name = sr_get_name(sr_info);
		sr_info_conf = sr_getc(sr_conf, daemon_name);
		svcname = sr_getc_str(sr_info, "svcname");
		arg = sr_getc_str(sr_info, "arg");
		if (!ecm_empty_str(svcname))
			sr_setc_str(sr_info_conf, "svcname", svcname);
		if (!ecm_empty_str(arg))
			sr_setc_str(sr_info_conf, "arg", arg);
	}
}

static void
add_static_route_arg(void)
{
	char	*p;
	pch_addr_t	pchaddr;
	sr_t		*sr_static_route;

	if (ecm_empty_str(ecm_optarg)) {
		remove_all_static_routes = ECM_TRUE;
		return;
	}

	if ((p = ecm_strchr(ecm_optarg, ',')) == NULL) {
		print_err("%s: invalid static route address spec", ecm_optarg);
		ecm_exit(1);
	}
	if (PCH_PARSEADDR(p + 1, &pchaddr) != EBC_OK) {
		print_err("%s: invalid static route address spec", p + 1);
		ecm_exit(1);
	}
	*p = '\0';
	sr_static_route = sr_createc_str(sr_static_routes, ecm_optarg, ST_STR, p + 1);
}

static void
parse_args(int argc, char *argv[])
{
	int	c;

	while ((c = ecm_getopt(argc, argv, "n:c:p:e:b:m:R:t:L:T:S:A:qfr:h")) != -1) {
		switch (c) {
			case 'n':
				ndname = ecm_strdup(ecm_optarg);
				break;
			case 'c':
				clname = ecm_strdup(ecm_optarg);
				break;
			case 'p':
				ecm_sscanf(ecm_optarg, "%hu", &ebc_port);
				break;
			case 'e':
				ecm_sscanf(ecm_optarg, "%hu", &egid_port);
				break;
			case 'b':
				bdcast_addr = ecm_strdup(ecm_optarg);
				break;
			case 'm':
				ecm_sscanf(ecm_optarg, "%u", &master_enabled);
				is_update_master_enabled = ECM_TRUE;
				break;
			case 'R':
				ecm_sscanf(ecm_optarg, "%u", &egid_refresh_rate);
				break;
			case 't':
				ecm_sscanf(ecm_optarg, "%u", &egid_session_timeout);
				break;
			case 'i':
				ecm_sscanf(ecm_optarg, "%u", &ebcd_detection_interval);
				break;
			case 'L':
				egid_login_page = ecm_strdup(ecm_optarg);
				break;
			case 'T':
				egid_top_page = ecm_strdup(ecm_optarg);
				break;
			case 'S':
				setup_service_name_arg(ecm_optarg, "svcname");
				break;
			case 'A':
				setup_service_name_arg(ecm_optarg, "arg");
				break;
			case 'q':
				verbose = ECM_FALSE;
				break;
			case 'f':
				force = ECM_TRUE;
				break;
			case 'r':
				add_static_route_arg();
				break;
			case 'h':
				usage();
				ecm_exit(0);
			default:
				usage();
				ecm_exit(1);
		}
	}

	if (ecm_optind < argc) {
		command = ecm_strdup(argv[ecm_optind]);
	}
}

static int
check_ecmconf(sr_t *sr_conf)
{
	sr_t	*sr_ebcd_conf;

	sr_ebcd_conf = sr_getc(sr_conf, "ebcd");
	if (ecm_empty_str(sr_getc_str(sr_ebcd_conf, "clname")))
		return 1;
	if (ecm_empty_str(sr_getc_str(sr_ebcd_conf, "ndname")))
		return 1;
	return 0;
}

static int
apply_ecmconf(sr_t *sr_conf, const char *econf_path)
{
	sr_t	*sr_ebcd_conf, *sr_edbd_conf, *sr_egid_conf;
	sr_t	*sr_static_routes_conf;

	sr_ebcd_conf = sr_getc(sr_conf, "ebcd");
	sr_edbd_conf = sr_getc(sr_conf, "edbd");
	sr_egid_conf = sr_getc(sr_conf, "egid");
	
        if (is_update_master_enabled) {
                sr_setc(sr_edbd_conf, "master_enabled", sr_create_bool(ST_BOOL, master_enabled));
        }

	if (!ecm_empty_str(clname))
		sr_setc_str(sr_ebcd_conf, "clname", clname);
	if (!ecm_empty_str(ndname))
		sr_setc_str(sr_ebcd_conf, "ndname", ndname);
	if (!ecm_empty_str(bdcast_addr))
		sr_setc_str(sr_ebcd_conf, "bdcast_addr", bdcast_addr);
	if (ebc_port != 0)
		sr_setc_int(sr_conf, "ebc_port", (ecm_uint32)(ebc_port));
	if (egid_port != 0)
		sr_setc_int(sr_egid_conf, "egid_port", (ecm_uint32)(egid_port));
	if (egid_refresh_rate >= 0)
		sr_setc_int(sr_egid_conf, "refresh_rate", (ecm_uint32)(egid_refresh_rate));
	if (egid_session_timeout >= 0)
		sr_setc_int(sr_egid_conf, "session_timeout", (ecm_uint32)(egid_session_timeout));
	if (!ecm_empty_str(egid_login_page))
		sr_setc_str(sr_egid_conf, "login_page", egid_login_page);
	if (!ecm_empty_str(egid_top_page))
		sr_setc_str(sr_egid_conf, "top_page", egid_top_page);

	apply_service_name_arg();

	sr_static_routes_conf = sr_getc(sr_ebcd_conf, "static_routes");
	if (remove_all_static_routes)
		sr_clear_child(sr_static_routes_conf);
	else {
		sr_t	*sr_static_route;

		SR_FOREACH_CHILD (sr_static_routes, NULL, sr_static_route) {
			const char	*ndname = sr_get_name(sr_static_route);
			if (sr_exist(sr_static_routes_conf, ndname)) {
				print_err("%s: duplicated static route node name", ndname);
				return 1;
			}
		}
		sr_merge(sr_static_routes_conf, sr_static_routes);
	}

	if (!eds_write_sr_conf(econf_path, sr_conf)) {
		print_err("cannot write ecmconf");
		return 1;
	}
	return 0;
}

int
main(int argc, char *argv[])
{
	eds_initialize();

	sr_daemon_infos = sr_create_dir();
	sr_createc_dir(sr_daemon_infos, "ebcd");
	sr_createc_dir(sr_daemon_infos, "edbd");
	sr_createc_dir(sr_daemon_infos, "egid");

	sr_static_routes = sr_create_dir();
	
	parse_args(argc, argv);

	econf_path = ecm_gen_path(ecm_default_clxdir(), "etc", "econf", NULL);
	if (!eds_read_sr_conf(econf_path, &sr_conf)) {
		print_err("%s: cannot open or wrong econf", econf_path);
		return 255;
	}

	if (ecm_strcmp(command, "check") == 0)
		return check_ecmconf(sr_conf);
	else if (ecm_strcmp(command, "update") == 0)
		return apply_ecmconf(sr_conf, econf_path);
	else if (ecm_strcmp(command, "install") == 0)
		return ecmconf_install(sr_conf);
	else if (ecm_strcmp(command, "remove") == 0)
		return ecmconf_remove();
	else if (ecm_strcmp(command, "status") == 0)
		return ecmconf_status(sr_conf);

	ecm_fprintf(ecm_stdout, "econf: %s\n", econf_path);
	sr_fancy_print(ecm_stdout, sr_conf, SR_RECUR, "");

	return 0;
}
